package com.example.adb_flutter

import android.app.Activity
import android.os.Build
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import com.example.adb_flutter.adb.AdbStream
import com.example.adb_flutter.adb.LocalServices
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.EventChannel.EventSink
import io.flutter.plugin.common.EventChannel.StreamHandler
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import org.lsposed.hiddenapibypass.HiddenApiBypass
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
import java.util.concurrent.Executors

class AdbFlutterPlugin: FlutterPlugin, MethodCallHandler, StreamHandler ,ActivityAware{
  private lateinit var activity: Activity
  private val executor = Executors.newFixedThreadPool(3)
  private var eventSink: EventSink? = null
  private lateinit var channel : MethodChannel
  private lateinit var eventChannel: EventChannel
  private var adbShellStream: AdbStream? = null
  private val outputGenerator = Runnable {
    try {
      BufferedReader(InputStreamReader(adbShellStream!!.openInputStream()))
        .use { reader ->
          var s: String?
          while (reader.readLine().also { s = it } != null) {
            val  map=HashMap<String,String>()
            map["commandOutput"]=s.toString()
            activity.runOnUiThread {
              eventSink?.success(map)
            }
          }
        }
    } catch (e: IOException) {
      e.printStackTrace()
    }
  }

  override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
    channel = MethodChannel(flutterPluginBinding.binaryMessenger, "adb_flutter")
    channel.setMethodCallHandler(this)
    eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "adb_flutter_event")
    eventChannel.setStreamHandler(this)
  }

  override fun onMethodCall(call: MethodCall, result: Result) {
    val args = call.arguments as Map<*, *>?
    when (call.method) {
        "connect" -> {
          val  map=HashMap<String,String>()
          executor.submit {
            try {
              val manager = AdbConnectionManager.getInstance(activity)
              if(manager.isConnected){
                map["connectionStatus"]=true.toString()
              }else{
                val connectionStatus: Boolean =  manager.connect(args!!["ipAddress"] as String, args["port"]as Int)
                map["connectionStatus"]=connectionStatus.toString()
              }

            } catch (th: Throwable) {
              th.printStackTrace()
              map["connectionStatus"]=false.toString()
            }
            activity.runOnUiThread {
              eventSink?.success(map)
            }
          }
          result.success("")
        }
        "autoConnect" -> {
          val  map=HashMap<String,String>()
          executor.submit {
            try {
              val manager =
                AdbConnectionManager.getInstance(activity)
              var connected = false
              manager.hostAddress= args!!["ipAddress"] as String
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                try {
                  connected = manager.autoConnect(activity, 5000)
                } catch (th: Throwable) {
                  th.printStackTrace()
                }
              }
              if (!connected) {
                connected = manager.connect(args["port"] as Int)
              }

              map["connectionStatus"]=connected.toString()

            } catch (th: Throwable) {
              map["connectionStatus"]=false.toString()
              th.printStackTrace()
            }
            activity.runOnUiThread {
              eventSink?.success(map)
            }
          }
          result.success("")
        }
        "close" -> {
          executor.submit {
            try {
              adbShellStream?.close()
              AdbConnectionManager.getInstance(activity)
                .close()
            } catch (e: Exception) {
              e.printStackTrace()
            }
          }
          executor.shutdown()
          result.success("")
        }
        "disconnect" -> {
          executor.submit {
            try {
              val manager =
                AdbConnectionManager.getInstance(activity)
              manager.disconnect()
            } catch (th: Throwable) {
              th.printStackTrace()
            }
          }
          result.success("")
        }
        "execute" -> {
          executor.submit {
            try {
              if (adbShellStream == null || adbShellStream!!.isClosed) {
                val manager =
                  AdbConnectionManager.getInstance(activity)
                adbShellStream = manager.openStream(LocalServices.SHELL)
                Thread(outputGenerator).start()
              }
              adbShellStream!!.openOutputStream()
                .use { os ->
                os.write(
                  String.format("%1\$s\n",  args!!["command"] as String)
                    .toByteArray(StandardCharsets.UTF_8)
                )
                os.flush()
                os.write("\n".toByteArray(StandardCharsets.UTF_8))
              }
            } catch (e: java.lang.Exception) {
              e.printStackTrace()
            }
          }
          result.success("")
        }
    }
  }

  override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    channel.setMethodCallHandler(null)
  }

  override fun onListen(arguments: Any?, events: EventSink?) {
    this.eventSink = events
  }

  override fun onCancel(arguments: Any?) {
    this.eventSink = null
  }

  override fun onAttachedToActivity(binding: ActivityPluginBinding) {
    activity=binding.activity
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
      HiddenApiBypass.addHiddenApiExemptions("L")
    }
  }

  override fun onDetachedFromActivityForConfigChanges() {
    TODO("Not yet implemented")
  }

  override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
    TODO("Not yet implemented")
  }

  override fun onDetachedFromActivity() {
    TODO("Not yet implemented")
  }
}
